package com.macro.mall.util.example;


import com.macro.mall.util.example.annotation.*;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

/**
 * @author EdisonHuang
 */
public class ExampleUtil {

    /**
     * 传入实体和example，根据实体的有值的字段，设置供查询的example和criteria
     * 注意事项 实体是mybatis-generator生成的，字段名不要更改
     * 需要模糊查询的字段，先约定为名字含有Name的 （后续可加入注解控制字段进行什么样的查询）
     * 功能实现的效果类似下面：
     * if (request.getCategorySeq() != null) {
     * criteria.andCategorySeqEqualTo(request.getCategorySeq());
     * }
     * if (request.getCategoryName() != null) {
     * criteria.andCategoryNameLike("%" + request.getCategoryName() + "%");
     * }
     *
     * @param entity  实体
     * @param example
     * @return example
     */
    public static <T, Z> Z getExample(T entity, Z example) {
        try {
            //获取实体的字段
            Field[] fields = entity.getClass().getDeclaredFields();
            //获取example的createCriteria方法
            Method createCriteria = example.getClass().getDeclaredMethod("createCriteria");
            createCriteria.setAccessible(true);
            //执行createCriteria方法
            Object criteria = createCriteria.invoke(example);
            //遍历设置有值的字段到criteria中
            for (Field field : fields) {
                String fieldName = field.getName();
                //设置首字母大写
                fieldName = fieldName.replaceFirst(fieldName.substring(0, 1), fieldName.substring(0, 1).toUpperCase(Locale.ROOT));
                field.setAccessible(true);
                Object o = field.get(entity);
                if (null != o) {
                    //对字段名含有name的进行模糊查询，不含Name的进行Equals条件查询（先约定大于配置，后续可加入注解控制字段）
                    if (fieldName.contains("Name") || fieldName.contains("name")) {
                        Method method = criteria.getClass().getDeclaredMethod("and" + fieldName + "Like", o.getClass());
                        method.setAccessible(true);
                        String likeStr = "%" + o + "%";
                        method.invoke(criteria, likeStr);
                    } else {
                        Method method = criteria.getClass().getMethod("and" + fieldName + "EqualTo", o.getClass());
                        method.setAccessible(true);
                        method.invoke(criteria, o);
                    }
                }
            }
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return example;
    }

    /**
     * 传入实体和example，根据实体的有值的字段，设置供查询的example和criteria
     * 注意事项 实体是mybatis-generator生成的，字段名不要更改
     * 升级版 可以通过注解来进行控制条件查询
     * <p>支持的注解：
     * "@EqualTo"
     * "@Like"
     * "@Null"
     * "@Than"
     * "@ThanOrEqualTo"
     * </>
     * <p>暂时不支持多个参数的条件查询
     * 比如：
     * "@Between"
     * "@In"
     * </p>
     *
     * @param entity
     * @param eClazz
     * @param <T>
     * @param <Z>
     * @return
     */
    public static <T, Z> Z getExampleByAnnotation(T entity, Class<Z> eClazz) {
        Z z = null;
        try {
            //获取实体的字段
            Field[] fields = entity.getClass().getDeclaredFields();
            //获取example的createCriteria方法
            Method createCriteria = eClazz.getDeclaredMethod("createCriteria");
            z = eClazz.newInstance();
            createCriteria.setAccessible(true);
            //执行createCriteria方法
            Object criteria = createCriteria.invoke(z);
            List<String> methods = new ArrayList<>();
            //遍历设置有值的字段到criteria中
            for (Field field : fields) {
                String fieldName = field.getName();
                field.setAccessible(true);
                IgnoreField ignoreField = field.getDeclaredAnnotation(IgnoreField.class);
                if (ignoreField != null) {
                    continue;
                }
                Class<?> type = field.getType();
                Object o = field.get(entity);
                if (null != o) {
                    //设置首字母大写
                    fieldName = fieldName.replaceFirst(fieldName.substring(0, 1), fieldName.substring(0, 1).toUpperCase(Locale.ROOT));
                    Annotation[] annotations = field.getDeclaredAnnotations();
                    String methodName = "";
                    for (Annotation annotation : annotations) {
                        Class<? extends Annotation> annotationClass = annotation.getClass();
                        if (Like.class.equals(annotation.annotationType())) {
                            Like like = (Like) annotation;
                            methodName = "and" + fieldName + like.NotFlag() + like.annotationType().getSimpleName();
                        } else if (In.class.equals(annotation.annotationType())) {
                            In in = (In) annotation;
                            methodName = "and" + fieldName + in.NotFlag() + in.annotationType().getSimpleName();
                        } else if (Null.class.equals(annotation.annotationType())) {
                            Null aNull = (Null) annotation;
                            methodName = "and" + fieldName + aNull.NotFlag() + aNull.annotationType().getSimpleName();
                        } else if (Than.class.equals(annotation.annotationType())) {
                            Than than = (Than) annotation;
                            methodName = "and" + fieldName + than.flag() + than.annotationType().getSimpleName();
                        } else if (Between.class.equals(annotation.annotationType())) {
                            Between between = (Between) annotation;
                            methodName = "and" + fieldName + between.flag() + between.annotationType().getSimpleName();
                        } else if (ThanOrEqualTo.class.equals(annotation.annotationType())) {
                            ThanOrEqualTo thanOrEqualTo = (ThanOrEqualTo) annotation;
                            methodName = "and" + fieldName + thanOrEqualTo.flag() + thanOrEqualTo.annotationType().getSimpleName();
                        }
                    }
                    if ("".equals(methodName)) {
                        methodName = "and" + fieldName + "EqualTo";
                    }
                    Method declaredMethod = criteria.getClass().getSuperclass().getDeclaredMethod(methodName, type);
                    declaredMethod.setAccessible(true);
                    if (methodName.contains("Like")) {
                        String likeStr = "%" + o + "%";
                        declaredMethod.invoke(criteria, likeStr);
                    } else {
                        Object cast = type.cast(o);
                        declaredMethod.invoke(criteria, cast);
                    }
                }
            }
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) {
            e.printStackTrace();
        }
        return z;
    }

//    public static void main(String[] args){
//        PosterCategoryRequest posterCategory = new PosterCategoryRequest();
//        posterCategory.setCategoryName("abc");
//        posterCategory.setCategorySeq(1);
//        posterCategory.setCategoryStatus(0);
//        PosterCategoryExample example = new PosterCategoryExample();
////        PosterCategoryExample posterCategoryExample = getExample(posterCategory, example);
//        PosterCategoryExample posterCategoryExample1 = getExampleByAnnotation(posterCategory, PosterCategoryExample.class);
//        System.out.println();
//    }
}
